class MyClass:
    class_attribute = "I'm a class attribute"

    def __init__(self, value):
            self.instance_attribute = value

    @classmethod
    def class_method(cls):
        print(f"Class method  called.Class attribute: {cls.class_attribute} ")

    def instance_method(self):
        print(f"Instance method called.Instance attribute: {self.instance_attribute}")


MyClass.class_method()

obj = MyClass("Hello")
obj.class_method()

obj.instance_method()

class BankAccount:
    def __init__(self, account_holder, initial_balance):
        self.account_holder = account_holder
        self.__balance = initial_balance

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"Deposited {amount}. New balance: {self.__balance}")
        else:
            print("Deposit amount must be positive")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"Withdrew {amount}. New balance: {self.__balance}")
        else:
            print("Invalid withdrawal amount")

    def get_balance(self):
        return self.__balance
    def __private_method(self):
        print("This is a private method")



account = BankAccount("Alice",1000)

print(account.account_holder)
print(account._BankAccount__balance)
print(account.get_balance())
account.deposit(500)
account.withdraw(200)

account._BankAccount__private_method()


class Animal:
    def __init__(self, name):
        self.name = name
        self.__private_attr = "Secret"
    def speak(self):
        raise NotImplementedError("Subclass must implement this method")

    def eat(self):
        print(f"{self.name} is eating")

    def __private_method(self):
        print("Private animal method")

class Dog(Animal):
    def __init__(self, name, bread):
        super().__init__(name)
        self.bread = bread

    def speak(self):
        print(f"{self.name} says:Woof!")

    def fetch(self):
        print(f"{self.name} is fetching the ball")

class Cat(Animal):
    def speak(self):
        print(f"{self.name} says:Meow!")

    def climb(self):
        print(f"{self.name} is climbing a tree")

dog = Dog("Buddy","Golden Retriever")
cat = Cat("Whiskers")

dog.eat()
cat.eat()

dog.speak()
cat.speak()

dog.fetch()
cat.climb()

print(dog.name)
print(cat.name)

print(dog.bread)

print(isinstance(dog, Animal))
print(isinstance(dog, Dog))
print(isinstance(dog, Cat))

print(issubclass(Dog, Animal))
print(issubclass(Dog, Cat))